﻿/**

 * Copyright (c) 2015-2017, FastDev 刘强 (fastdev@163.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

using InjectedLibs;
using Mono.Cecil;
using Mono.Cecil.Cil;
using OF.ILModifier;
using OF.ILModifier.Common;
using OF.ILModifier.Entity;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using TestConsole;
       
namespace RunIL
{
    public class Program
    {
        /*
        public static void ViewCods()
        {
            Action<string, Func<string, bool>, Func<object, bool>, Action<string, MethodDefinition>> testAction = ILHelper.SearchDirCodeStrings;
            MethodInfo viewMethod = testAction.Method;

            string path = "OF.ILModifier.dll";
            ILBuilder builder = new ILBuilder(path);

            List<string> resultList = MethodHelper.GetMethodCodes(viewMethod, builder);
            Console.WriteLine(string.Join(Environment.NewLine, resultList));
            Console.ReadLine();
        }
        */

        private static void TestInjectAllProxy(ILBuilder builder)
        {
            MethodInfo[] methods = typeof(TestsProxy).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.CreateInstance);
            methods = methods.Where(method => method.Name.StartsWith("Run", StringComparison.InvariantCultureIgnoreCase) && method.Name.Length > 3).ToArray();
            foreach (MethodInfo mi in methods)
            {
                Func<ParamContainer, ParamContainer> proxyHandler = null;
                MethodInfo callMethod = null;
                if (mi.IsGenericMethod)
                {
                    callMethod = mi.GetGenericMethodDefinition();
                }
                else
                {
                    callMethod = mi;
                }
                if(mi.ReturnType.Name != "System.Void")
                {
                    proxyHandler = InjectedProxy.ProxyFuncNoResult;
                }
                else
                {
                    proxyHandler = InjectedProxy.ProxyFuncNoResult;
                }
                MethodHelper.InjectProxy(builder, callMethod, proxyHandler, InjectedWrap.OnException);
            }
            /*
            Func<TestEntity, string> callAct = TestsProxy.RunStaticGeneric<TestEntity, string>;
            MethodInfo callMethod = callAct.Method.GetGenericMethodDefinition();
            MethodHelper.InjectProxy(builder, callMethod, InjectedProxy.ProxyFunc, InjectedWrap.OnException);
            */
        }

        private static void TestVirtualCallProxy(ILBuilder builder)
        {
            Action callAct = new TestsProxy().VirtualTest;
            MethodInfo callMethod = callAct.Method;
            MethodHelper.InjectProxy(builder, callMethod, InjectedProxy.ProxyFuncNoResult, InjectedWrap.OnException);
        }

        private static void TestVirtualCallWrap(ILBuilder builder)
        {
            Action action = new TestsWrap().VirtualTest;
            MethodInfo sourceMethod = action.Method;

            Action<ParamContainer> trace = InjectedWrap.Before;
            MethodInfo beforeMethod = trace.Method;

            trace = InjectedWrap.After;
            MethodInfo afterMethod = trace.Method;

            action = new InjectedWrap().VirtualCallReplaced;
            MethodInfo replaceMethod = action.Method;
            MethodHelper.InjectWrapEvents(builder, beforeMethod, sourceMethod, (oldMethod) => replaceMethod, afterMethod);
        }

        private static void TestWrapAction(ILBuilder builder)
        {
            Action<TestEntity> action = TestsWrap.RunInjectWrapEvents;
            MethodInfo methodInfo = action.Method;
            MethodInfo sourceMethod = methodInfo;

            Action<ParamContainer> trace = InjectedWrap.Before;
            MethodInfo beforeMethod = trace.Method;

            trace = InjectedWrap.After;
            MethodInfo afterMethod = trace.Method;

            action = InjectedWrap.WrapActionReplace;
            MethodInfo replaceMethod = action.Method;
            MethodHelper.InjectWrapEvents(builder, beforeMethod, sourceMethod, (oldMethod) => replaceMethod, afterMethod);
        }

        private static void TestWrapStatGenActionStatGenReplace(ILBuilder builder)
        {
            Action<TestEntity> action = TestsWrap.RunWrapStatGenActionStatGenReplace;
            MethodInfo methodInfo = action.Method;
            MethodInfo sourceMethod = methodInfo;

            Action<ParamContainer> trace = InjectedWrap.Before;
            MethodInfo beforeMethod = trace.Method;

            trace = InjectedWrap.After;
            MethodInfo afterMethod = trace.Method;


            action = InjectedWrap.RunWrapStatGenFuncStatGenReplace;
            MethodInfo replaceMethod = action.Method;

            MethodHelper.InjectWrapEvents(builder, beforeMethod, sourceMethod, (oldMethod) => replaceMethod, afterMethod);
        }

        private static void TestWrapInstActionInstReplace(ILBuilder builder)
        {
            Action<TestEntity> action = new TestsWrap().RunWrapInstActionInstReplace;
            MethodInfo methodInfo = action.Method;
            MethodInfo sourceMethod = methodInfo;

            Action<ParamContainer> trace = InjectedWrap.Before;
            MethodInfo beforeMethod = trace.Method;

            trace = InjectedWrap.After;
            MethodInfo afterMethod = trace.Method;

            action = new InjectedWrap().WrapInstActionInstReplaced;
            MethodInfo replaceMethod = action.Method;

            MethodHelper.InjectWrapEvents(builder, beforeMethod, sourceMethod, (oldMethod) => replaceMethod, afterMethod);
        }

        private static void TestWrapInstGenActionInstGenReplace(ILBuilder builder)
        {
            Action<TestEntity> action = new TestsWrap().RunWrapInstGenActionInstGenReplace;
            MethodInfo methodInfo = action.Method;
            MethodInfo sourceMethod = methodInfo;

            Action<ParamContainer> trace = InjectedWrap.Before;
            MethodInfo beforeMethod = trace.Method;

            trace = InjectedWrap.After;
            MethodInfo afterMethod = trace.Method;

            action = new InjectedWrap().WrapInstGenActionInstGenReplaced;
            MethodInfo replaceMethod = action.Method;

            MethodHelper.InjectWrapEvents(builder, beforeMethod, sourceMethod, (oldMethod) => replaceMethod, afterMethod);
        }


        private static void TestWrapInstFuncInstReplace(ILBuilder builder)
        {
            Func<TestEntity, string> action = new TestsWrap().RunWrapInstFuncInstReplace;
            MethodInfo methodInfo = action.Method;
            MethodInfo sourceMethod = methodInfo;

            Action<ParamContainer> trace = InjectedWrap.Before;
            MethodInfo beforeMethod = trace.Method;

            trace = InjectedWrap.After;
            MethodInfo afterMethod = trace.Method;

            action = new InjectedWrap().WrapInstFuncInstReplace;
            MethodInfo replaceMethod = action.Method;

            MethodHelper.InjectWrapEvents(builder, beforeMethod, sourceMethod, (oldMethod) => replaceMethod, afterMethod);
        }



        private static void TestWrapInstGenFuncInstGenReplace(ILBuilder builder)
        {
            Func<TestEntity, string> action = new TestsWrap().RunWrapInstGenFuncInstGenReplace<TestEntity, string>;
            MethodInfo methodInfo = action.Method;
            MethodInfo sourceMethod = methodInfo;

            Action<ParamContainer> trace = InjectedWrap.Before;
            MethodInfo beforeMethod = trace.Method;

            trace = InjectedWrap.After;
            MethodInfo afterMethod = trace.Method;

            action = new InjectedWrap().WrapInstGenFuncInstGenReplace<TestEntity, string>;
            MethodInfo replaceMethod = action.Method;

            MethodHelper.InjectWrapEvents(builder, beforeMethod, sourceMethod, (oldMethod) => replaceMethod, afterMethod);
        }

        private static void TestWrapStatFuncStatReplace(ILBuilder builder)
        {
            Func<TestEntity, string> action = TestsWrap.RunWrapStatFuncStatReplace;
            MethodInfo methodInfo = action.Method;
            MethodInfo sourceMethod = methodInfo;

            Action<ParamContainer> trace = InjectedWrap.Before;
            MethodInfo beforeMethod = trace.Method;

            trace = InjectedWrap.After;
            MethodInfo afterMethod = trace.Method;


            action = InjectedWrap.WrapStatFuncStatReplace;
            MethodInfo replaceMethod = action.Method;

            MethodHelper.InjectWrapEvents(builder, beforeMethod, sourceMethod, (oldMethod) => replaceMethod, afterMethod);
        }



        private static void TestWrapStatGenFuncStatGenReplace(ILBuilder builder)
        {
            Func<TestEntity, string> action = TestsWrap.RunWrapStatGenFuncStatGenReplace<TestEntity, string>;
            MethodInfo methodInfo = action.Method;
            MethodInfo sourceMethod = methodInfo;

            Action<ParamContainer> trace = InjectedWrap.Before;
            MethodInfo beforeMethod = trace.Method;

            trace = InjectedWrap.After;
            MethodInfo afterMethod = trace.Method;

            action = InjectedWrap.WrapStatGenActionStatGenReplacedFunc<TestEntity, string>;
            MethodInfo replaceMethod = action.Method;

            MethodHelper.InjectWrapEvents(builder, beforeMethod, sourceMethod, (oldMethod) => replaceMethod, afterMethod);
        }



        static void TestReplaceCall(ILBuilder builder)
        {
            Action inAct = TestsReplace.RunInsCallBeReplaced;
            MethodInfo inMethod = inAct.Method;

            Func<TestEntity, string> callAct = TestsReplace.CallToBeReplacedTarget<TestEntity, string>;
            MethodInfo callMethod = callAct.Method;

            Func<TestEntity, string> replaceAct = InjectedReplace.ReplaceCallNew<TestEntity, string>;
            MethodInfo replaceActMethod = replaceAct.Method;

            MethodHelper.ReplaceCallIns(builder,
                inMethod, callMethod, replaceActMethod);
        }


        static void Main(string[] args)
        {
            TestAll(); 
            /*
            TestVirtualCall();
            */
        }
        
        private static void TestVirtualCall() 
        {
            ILBuilder builder = new ILBuilder(@"TestConsole.exe");
            TestVirtualCallProxy(builder);
            TestVirtualCallWrap(builder);
            builder.Save("TestConsole2.exe");
        }

        private static void TestProxyResultOrNot()
        {
            ILBuilder builder = new ILBuilder(@"TestConsole.exe");
            Func<string> callAct = TestsProxy.RunStaticFunc;
            MethodInfo callMethod = callAct.Method;
            //MethodHelper.InjectProxy(builder, callMethod, InjectedProxy.ProxyFuncNoResult, InjectedWrap.OnException);
            MethodHelper.InjectProxy(builder, callMethod, InjectedProxy.ProxyFunc, InjectedWrap.OnException);
            builder.Save("TestConsole2.exe");
        }

        private static void TestAll()
        {
            //TestProxyResultOrNot();
            ILBuilder builder = new ILBuilder(@"TestConsole.exe");
            TestWrapStatGenFuncStatGenReplace(builder);
            TestWrapAction(builder);
            TestWrapStatGenActionStatGenReplace(builder);
            TestWrapInstActionInstReplace(builder);
            TestWrapInstGenActionInstGenReplace(builder);
            TestWrapInstFuncInstReplace(builder);
            TestWrapStatFuncStatReplace(builder);
            TestWrapMulParam(builder);
            TestWrapInstGenFuncInstGenReplace(builder);
            TestInjectAllProxy(builder);
            TestReplaceCall(builder);
            TestVirtualCallProxy(builder);
            TestVirtualCallWrap(builder);
            builder.Save("TestConsole2.exe");
        }



        public static void TestWrapMulParam(ILBuilder builder)
        {
            Func<int, float, string, double, decimal, short, string> func = new TestsWrap().RunGetMulParam;
            MethodInfo sourceMethod = func.Method;

            Action<ParamContainer> action = InjectedWrap.Before;
            MethodInfo beforeMethod = action.Method;

            action = InjectedWrap.After;
            MethodInfo afterMethod = action.Method;

            MethodHelper.InjectWrapEvents(builder, beforeMethod, sourceMethod, (oldMethod) => null, afterMethod);
        }
    }
}

